--wuqibo
--2020.12.29
--转盘驱动类(支持多少度回调一次，支持闲置时转着玩^_^)
--[[
  使用方法:
    self.turntableDriver = TurntableDriver:new(self.transform)
    self.turntableDriver:start(停止角度, self.onStop, self)
    或
    self.turntableDriver:start(停止角度, function()
        --停止了
    end)
    
    ●●● 其他可选方法：
    ● 立即停止在某个角度
    self.turntableDriver:stopAtAngle(10)
    
    ● 每旋转多少角度触发一次回调(可播放弹簧机械音效等)，如每旋转10度回调一次
    self.turntableDriver:onOverAngle(10, self.onOverAngle, self)
    
    ● 对外属性/方法：
    1 是否在转动中(只读)：bool isRunning = self.turntableDriver:isRunning()
    2 设置闲置时是否可以拖动旋转：self.turntableDriver.isDragEnable = true/false
    3 销毁属性，停止Update的监听: self.turntableDriver:destroy()
]]
Mathf = UnityEngine.Mathf
Screen = UnityEngine.Screen
DOTween = DG.Tweening.DOTween
RotateMode = DG.Tweening.RotateMode
Core = DG.Tweening.Core

local TurntableDriver = fb_class("TurntableDriver")

local function screenToUIPoint(screenPos)
    local ScreenUISize = Vector2(1920, 1080)
    local x = ScreenUISize.x * (screenPos.x - Screen.width * 0.5) / Screen.width
    local y = ScreenUISize.y * (screenPos.y - Screen.height * 0.5) / Screen.height
    return Vector2(x, y)
end

local function getRootPos(rectTransform)
    local pos = rectTransform.anchoredPosition
    local parent = rectTransform
    local canvas = parent:GetComponent("Canvas")
    while (not canvas) do
        parent = parent.parent
        canvas = parent:GetComponent("Canvas")
        if not canvas then
            pos = pos + parent.anchoredPosition
        end
    end
    return pos
end

local function angle360(angle)
    while angle < 0 do
        angle = angle + 360
    end
    while angle > 360 do
        angle = angle - 360
    end
    return angle
end

--transform：旋转目标
--decelRounds：减速阶段所需圈数（默认6）
--decelSeconds：减速阶段所需秒数（默认4）
--isReverse：是否逆时针方向旋转（默认false）
function TurntableDriver:ctor(transform, decelRounds, decelSeconds, isReverse)
    self.transform = transform
    self.decelRounds = decelRounds or 6
    self.decelSeconds = decelSeconds or 4
    self.isReverse = isReverse
    self.transAngle = self.transform.localEulerAngles
    self.isDragEnable = true
    self._dragDelta = 0
    self.tweenId = self.transform:GetInstanceID()

    self.center = getRootPos(self.transform)
    UIEventListener.Get(self.transform).onPointerDown =
        UIEventListener.PointerEventDelegate(
        function()
            if self.isDragEnable and not self._isRunning then
                local downPos = screenToUIPoint(Input.mousePosition)
                local yDis = downPos.y - self.center.y
                local xDis = downPos.x - self.center.x
                self.previousAngle = Mathf.Atan2(yDis, xDis) * Mathf.Rad2Deg
                self._canFreeDrag = true
                self._dragDelta = 0
            end
        end
    )

    if self.transform then
        self.updateHandler = UpdateBeat:CreateListener(self.update, self)
        UpdateBeat:AddListener(self.updateHandler)
    end
end

--初始化到原始角度
function TurntableDriver:initAngle()
    self.transAngle.z = 0
    self.transform.localEulerAngles = self.transAngle
end

--开始旋转转盘
--stopAngle：停在目标角度
function TurntableDriver:start(stopAngle, onStop, selfObject)
    if self._isRunning then
        return self
    end

    --Log("stopAngle: " .. stopAngle)
    self.onStop = onStop
    self.selfObject = selfObject
    self._canFreeDrag = false
    self._dragDelta = 0

    self.counterPreviousAngle = self.transform.localEulerAngles.z
    self.onOverAngleCounter = 0
    self._isRunning = true
    local _stopAngle = stopAngle - self.decelRounds * 360
    if self.isReverse then
        _stopAngle = stopAngle + self.decelRounds * 360
    end
    self.transform:DORotate(Vector3(0, 0, _stopAngle), self.decelSeconds, RotateMode.FastBeyond360):SetId(self.tweenId):OnUpdate(
        function()
            self:listenAddAngle()
        end
    ):OnComplete(
        function()
            if self.onStop then
                if self.selfObject then
                    self.onStop(self.selfObject)
                else
                    self.onStop()
                end
            end
            coroutine.start(
                function()
                    coroutine.wait(1)
                    self._isRunning = false
                end
            )
        end
    )
    return self
end

--对外函数：判断转盘是否在正式抽奖转动中
function TurntableDriver:isRunning()
    return self._isRunning
end

--每圈越过0-360度的指定度数时触发一次回调
function TurntableDriver:onOverAngle(angle, callbackFunc, selfObject)
    self.onOverAngleTargetAngle = angle
    self.onOverAngleFunc = callbackFunc
    self.onOverAngleSelf = selfObject
    return self
end

--不等转完，立即结束并停在特定的角度
function TurntableDriver:stopAtAngle(stopAngle)
    DOTween.Kill(self.tweenId)
    self._isRunning = false
    self._dragDelta = 0
    self.transAngle.z = stopAngle
    self.transform.localEulerAngles = self.transAngle
    return self
end

function TurntableDriver:update()
    if not self._isRunning then
        if self.transform == nil or self.transform:Equals(nil) then
            self:destroy()
            return
        end
        --自由转动
        if self._canFreeDrag then
            local downPos = screenToUIPoint(Input.mousePosition)
            local yDis = downPos.y - self.center.y
            local xDis = downPos.x - self.center.x
            local angle = Mathf.Atan2(yDis, xDis) * Mathf.Rad2Deg
            self._dragDelta = angle - self.previousAngle
            self.previousAngle = angle
            self.transAngle.z = self.transAngle.z + self._dragDelta
            self.transform.localEulerAngles = self.transAngle
            if Input.GetMouseButtonUp(0) then
                self._canFreeDrag = false
            end
        else
            self._dragDelta = self._dragDelta + (0 - self._dragDelta) * 0.05
            self.transAngle.z = self.transform.localEulerAngles.z
            self.transAngle.z = self.transAngle.z + self._dragDelta
            self.transform.localEulerAngles = self.transAngle
        end
    end
end

function TurntableDriver:listenAddAngle()
    if self.onOverAngleFunc then
        local currAngle = angle360(self.transform.localEulerAngles.z)
        local addValue = self.counterPreviousAngle - currAngle
        self.counterPreviousAngle = currAngle
        self:addAngles(math.abs(addValue))
    end
end

function TurntableDriver:addAngles(addValue)
    self.onOverAngleCounter = self.onOverAngleCounter + addValue
    if self.onOverAngleCounter >= self.onOverAngleTargetAngle then
        self.onOverAngleCounter = 0
        if self.onOverAngleSelf then
            self.onOverAngleFunc(self.onOverAngleSelf)
        else
            self.onOverAngleFunc()
        end
    end
end

--销毁并停止Update监听
function TurntableDriver:destroy()
    DOTween.Kill(self.tweenId)
    UpdateBeat:RemoveListener(self.updateHandler)
    fb_clear(self)
    return self
end

return TurntableDriver
